﻿using UnityEngine;

namespace HotPlayer.Physic2D
{
    /**
     * 视频教程
     * https://www.bilibili.com/video/BV16P4y1q7GZ/?spm_id_from=333.337.search-card.all.click&vd_source=661501b63987458f6eb85622797a9161
     *
     * 2D
     * https://github.com/kroitor/gjk.c?tab=readme-ov-file
     * https://dyn4j.org/2010/04/gjk-gilbert-johnson-keerthi/
     *
     * 3D
     * https://github.com/MattiaMontanari/openGJK?tab=readme-ov-file
     *
     * 查找深度、法线、碰撞点
     * https://winter.dev/articles/epa-algorithm
     * https://www.youtube.com/watch?v=0XQ2FSz3EK8
     *
     * GJK2D
     * https://zhuanlan.zhihu.com/p/511164248
     * 
     * GJK 与 EPA 算法
     * https://zhuanlan.zhihu.com/p/603328999
     */
    public static class GJK
    {
        public static bool TestCollide(Polygon2D shape1, Polygon2D shape2)
        {
            var index = 0;
            Vector2 a, b, c, d, ao, ab, ac, abperp, acperp;
            var simplex = GJKUtils.cleanSimplex;

            var position1 = shape1.AveragePoint(); // not a CoG but
            var position2 = shape2.AveragePoint(); // it's ok for GJK )

            d = position1 - position2;

            if ((d.x == 0) && (d.y == 0))
                d.x = 1f;

            a = GJKUtils.Support(shape1, shape2, d);
            simplex[0] = a;

            if (Vector2.Dot(a, d) <= 0)
                return false; // no collision
            d = -a; // The next search direction is always towards the origin, so the next search direction is negate(a)

            var loopHunter = 0;
            var loopLimit = 1024 * (shape1.pointCount + shape2.pointCount);
            while (loopHunter++ < loopLimit)
            {
                a = GJKUtils.Support(shape1, shape2, d);
                simplex[++index] = a;

                if (Vector2.Dot(a, d) <= 0)
                    return false; // no collision

                ao = -a;

                if (index < 2)
                {
                    b = simplex[0];
                    ab = b - a;
                    d = MathUtils.TripleProduct(ab, ao, ab);
                    if (d.sqrMagnitude == 0)
                    {
                        d = Vector2.Perpendicular(ab);
                    }

                    continue;
                }

                b = simplex[1];
                c = simplex[0];
                ab = b - a;
                ac = c - a;

                acperp = MathUtils.TripleProduct(ab, ac, ac);

                if (Vector2.Dot(acperp, ao) >= 0)
                {
                    d = acperp;
                }
                else
                {
                    abperp = MathUtils.TripleProduct(ac, ab, ab);

                    if (Vector2.Dot(abperp, ao) < 0)
                    {
                        return true; //collision
                    }

                    simplex[0] = simplex[1];// swap first element (point C)

                    d = abperp; // new direction is normal to AB towards Origin
                }

                simplex[1] = simplex[2];
                index--;
            }

            return false;
        }
    }
}